<?php

namespace App;

use Doctrine\ORM\Exception\ORMException;
use Doctrine\ORM\OptimisticLockException;
use JsonLowCode\Schema;
use JsonLowCode\Table;
use JsonLowCode\UI;
use Library\Contacts\CrudInterface;
use Library\OrmRepository;
use Library\Request;
use Library\Response;

abstract class CrudController extends Controller implements CrudInterface
{
    protected string $title;
    protected string $modelClass;
    protected string $alias = "e";

    protected OrmRepository $ormRepo;
    protected Request $request;

    public function __construct(){
        $this->ormRepo = OrmRepository::make($this->modelClass);
        $this->request = Request::make();
    }


    /**
     * @return Response
     */
    public function index(): Response
    {
        $table = Table::make(function (Table $table){
            $this->table($table);
//            $table->slotSearch($this->search($table),['search']);
            $table->fetchClosure(fn ($requestField, $responseField, $rowClosure) => $this->fetchList($requestField,$responseField, $rowClosure));
        });
        $page = UI::page(UI::card($this->title, $table));
        $content = UI::content($page);
        return $this->result(compact("content"));
    }

    /**
     * @param array $requestField
     * @param array $responseField
     * @param \Closure|null $rowClosure
     * @return array
     * @throws \Exception
     */
    protected function fetchList(array $requestField, array $responseField, \Closure $rowClosure = null): array
    {
        $alias = $this->alias;
        $pageField = $requestField["pageField"]??"page";
        $pageSizeField = $requestField["pageSizeField"]??"pageSize";
        $page = $this->request->get($pageField);
        $pageSize = $this->request->get($pageSizeField);
        list($whereMap,$valueMap) = $this->buildWhere($alias,[]);
        $builder = $this->ormRepo->build($this->alias);
        if ($whereMap){
            $builder->where(implode(" AND ",$whereMap));
        }
        $result = $builder->setBindParams($valueMap)->paginator($page, $pageSize);
        if ($rowClosure instanceof \Closure){
            foreach ($result['items'] as $key=>$row){
                $result['items'][$key] = tap($row, $rowClosure);
            }
        }
        return $this->handleResponse($responseField,$result);
    }

    /**
     * @return Response
     */
    public function detail(): Response
    {
        $id = $this->request->post("id");
        $detail = $this->ormRepo->find($id);
        return $this->result(compact("detail"));
    }


    /**
     * @return Response
     */
    public function create(): Response
    {
        try {
            $model = new $this->modelClass();
            $rows = $this->request->post("rows");
            $this->ormRepo->update($model, $rows);
        }catch (OptimisticLockException|ORMException $e){
            return $this->error($e->getMessage());
        }
        return $this->success("创建成功");
    }


    /**
     * @return Response
     */
    public function update(): Response
    {
        try {
            $id = $this->request->post("id");
            $rows = $this->request->post("rows");
            $model = $this->ormRepo->find($id);
            $this->ormRepo->update($model, $rows);
        }catch (OptimisticLockException|ORMException $e){
            return $this->error($e->getMessage());
        }
        return $this->success("更新成功");
    }


    /**
     * @param string $alias
     * @param array $search
     * @return array
     */
    protected function buildWhere(string $alias, array $search):array
    {
        $whereMap = [];
        $valueMap = [];
        if ($search){
            foreach ($search as $field=>$value){
                $whereMap[] = "$alias.$field = :$field";
                $valueMap[$field] = $value;
            }
        }
        return [$whereMap,$valueMap];
    }

    /**
     * @param array $responseField
     * @param array $result
     * @param array $others
     * @return array
     */
    protected function handleResponse(array $responseField, array $result, array $others = []):array
    {
        $totalField = $responseField["totalField"]??"total";
        $pageCountField = $responseField["pageCountField"]??"pageCount";
        $dataField = $responseField["dataField"]??"items";
        return array_merge([
            $totalField=>$result['total'],
            $pageCountField=>$result['pageCount'],
            $dataField=>$result['items'],
        ],$others);
    }


}